寬廣的室外網球場上,學生們正在做發球考試的練習。
「嘿!」女孩左手將球向上輕拋,右手握拍奮力用全身的力量擊球。
和羽球重視甩腕的方式不同,網球需要軀幹旋轉的力量,否則很容易受傷。
球網對面的練習搭擋迅速撿球發回,這次換成女孩緊盯著球的飛行拋物線。
「幸好發球規則有指定落點要在對角發球區,雙人練習就不用來回奔波。」詩憶鬆了口氣,彎腰把還在滾動的球撿起來。「嗯?這麼說起來,函式參數不是一開始就要指定類別嗎?那Collections
的add
函式是怎麼設計成可以根據成員類別彈性變化?」
她一邊思考一邊發球,一不留神就把球打出了界外,最後收到了搭擋的憤怒警告,她只好先甩開腦裡的困惑,畢竟球場上到處都是飛球,很危險的。
很快的,又到了晚上的補課時間。
唯心聽完詩憶的問題,很高興她開始深入思考程式的架構。
「妳察覺到今天的主題,泛型
的存在了。」唯心用食指指節輕輕敲擊白板剛寫上的Generics
。「泛型
,就是將類別也當作一種參數宣告,宣告時需要放在大小於符號中間,比如List
就是宣告成List<T>
,要指定T
的時候則用List<String>
、List<Drink>
等的寫法。」
//可以變動的飲料清單
val drinks: MutableList<Drink>
為了更詳盡的說明,她打開標準函式庫裡的List
的程式碼檔案給詩憶參考。「對了,泛型
參數名字不一定要用Type
的T
,像有些文件寫的是List<E>
,E
來自Elements
。另一個常用的名字是Result
的R
。」
public interface List<out E> : Collection<E> {
/**
* Returns the element at the specified index in the list.
*/
public operator fun get(index: Int): E
/**
* Returns a view of the portion of this list between the specified [fromIndex] (inclusive) and [toIndex] (exclusive).
* The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
*
* Structural changes in the base list make the behavior of the view undefined.
*/
public fun subList(fromIndex: Int, toIndex: Int): List<E>
}
「妳看,因為宣告了List<out E>
,所以取得裡面的元素就可以宣告成public operator fun get(index: Int): E
。」唯心指了指其中最簡單的函式。
「學姐,可是我看程式碼裡不是List<E>
而是List<out E>
?」詩憶馬上發現了問題。
「喔,List<E>
就是List<out E>
和List<in E>
的聯集,out
的意思是限定函式只能把該泛型放在回傳的位置,in
是限定函式把該泛型放在參數的位置。所以可以變動成員的MutableList<E>
就還需要支援in
的部分。」唯心將List.kt
的程式碼向下滾動到MutableList
的部分。
public interface MutableList<E> : List<E>, MutableCollection<E> { /**
* Adds the specified element to the end of this list.
*
* @return `true` because the list is always modified as the result of this operation.
*/
override fun add(element: E): Boolean
override fun remove(element: E): Boolean
}
唯心瞥了一眼手上的講義,補充說。「而如果要支援多個泛型,和函式參數一樣,用逗號分隔。此外,泛型也可以限定範圍,比如說希望都是繼承某個類別的類型或介面。」
public interface Sample<T : 某個可繼承的類型或介面, R> {
}
詩憶往前翻了翻繼承和介面相關的筆記。「之前說類別可以擴展多個介面,那泛型也可以限定多個介面嗎?」
「可以,不過語法比較複雜,課堂上沒特別提,我也沒怎麼用過,我找找官網文件唷。」唯心很快找到了說明泛型
的頁面。「要用where
描述,這裡有個範例可以參考。」
https://kotlinlang.org/docs/generics.html#upper-bounds
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}